package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/tendermint/tendermint/abci/example/kvstore"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/tendermint/tendermint/my_test/config"
	"github.com/tendermint/tendermint/my_test/entity"
	"github.com/tendermint/tendermint/my_test/utils"

	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
	"github.com/tendermint/tendermint/rpc/client/http"
)

// 将字符串形式的向量转换为float32切片
func parseVectorString(vectorStr string) []float32 {
	values := strings.Split(vectorStr, ",")
	result := make([]float32, len(values))
	for i, v := range values {
		x, _ := strconv.ParseFloat(v, 64)
		result[i] = float32(x)
	}
	return result
}

// 获取文件类别
func getFileCategory(filename string) tmproto.FileCategory {
	categoryMap := map[string]tmproto.FileCategory{
		"处方表":    tmproto.FileCategory_Prescription,
		"处方明细表":  tmproto.FileCategory_Prescription_Detail,
		"处方签名表":  tmproto.FileCategory_prescription_Signature,
		"处方审方表":  tmproto.FileCategory_prescription_Audit,
		"就诊记录表":  tmproto.FileCategory_Visit_Record,
		"心电图序列表": tmproto.FileCategory_Ecg_Sequence,
		"CT影像表":  tmproto.FileCategory_Medical_Image,
	}

	for key, value := range categoryMap {
		if strings.Contains(filename, key) {
			return value
		}
	}
	return -1
}

// 获取文件处理优先级
func getFilePriority(filename string) int {
	categoryMap := map[string]int{
		"处方表":    1, // 最高优先级，最先处理
		"就诊记录表":  2, // 第二优先级
		"处方明细表":  3, // 依赖处方表
		"处方审方表":  3, // 依赖处方表
		"处方签名表":  4, // 独立，可以随时处理
		"CT影像表":  5, // 依赖就诊记录表
		"心电图序列表": 5, // 依赖就诊记录表
	}

	for key, priority := range categoryMap {
		if strings.Contains(filename, key) {
			return priority
		}
	}
	return 999 // 未知类别最低优先级
}

func main() {
	rand.Seed(time.Now().Unix())

	cli, err := http.New(config.DefaultIPAndPort, "/websocket")
	if err != nil {
		panic(err)
	}

	dir := filepath.Join(os.Getenv("HOME"), config.DirName)

	// 读取所有JSON文件
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		panic(err)
	}

	// 按优先级对文件进行排序
	type FileInfo struct {
		file     os.FileInfo
		priority int
	}

	var fileList []FileInfo
	for _, file := range files {
		if !strings.HasSuffix(file.Name(), ".json") {
			continue
		}
		fileList = append(fileList, FileInfo{
			file:     file,
			priority: getFilePriority(file.Name()),
		})
	}

	// 按优先级排序
	sort.Slice(fileList, func(i, j int) bool {
		return fileList[i].priority < fileList[j].priority
	})

	// 按排序后的顺序处理文件
	for _, fileInfo := range fileList {
		file := fileInfo.file

		// 读取JSON文件内容
		jsonData, err := os.ReadFile(filepath.Join(dir, file.Name()))
		if err != nil {
			fmt.Printf("读取文件 %s 失败: %v\n", file.Name(), err)
			continue
		}

		// 获取文件类别
		fileCategory := getFileCategory(file.Name())
		if fileCategory == -1 {
			fmt.Printf("无法识别的文件类别: %s\n", file.Name())
			continue
		}

		// 根据文件类别处理特殊字段
		var (
			objectId   string
			eigenvalue []float32
			dataType   tmproto.DataType
		)

		switch fileCategory {
		case tmproto.FileCategory_Ecg_Sequence:
			var ecgJSON entity.ECGSequenceJSON
			if err := json.Unmarshal(jsonData, &ecgJSON); err != nil {
				fmt.Printf("解析心电图序列JSON失败 %s: %v\n", file.Name(), err)
				continue
			}
			objectId = fmt.Sprintf("%v", ecgJSON.ID)
			eigenvalue = parseVectorString(ecgJSON.SeqVector)
			dataType = tmproto.DataType_SERIES

		case tmproto.FileCategory_Medical_Image:
			var ctJSON entity.CTImagingJSON
			if err := json.Unmarshal(jsonData, &ctJSON); err != nil {
				fmt.Printf("解析CT影像JSON失败 %s: %v\n", file.Name(), err)
				continue
			}
			objectId = fmt.Sprintf("%v", ctJSON.ID)
			eigenvalue = parseVectorString(ctJSON.PicVector)
			dataType = tmproto.DataType_IMAGE

		case tmproto.FileCategory_Prescription_Detail:
			var detailJSON entity.PrescriptionDetailJSON
			if err := json.Unmarshal(jsonData, &detailJSON); err != nil {
				fmt.Printf("解析处方明细JSON失败 %s: %v\n", file.Name(), err)
				continue
			}

			// 注意，处方明细表的objectId是药品Id，用来溯源查询
			objectId = fmt.Sprintf("%v", detailJSON.ItemID)

			fmt.Println(objectId)

			eigenvalue = utils.RandomPoint()
			dataType = tmproto.DataType_TEXT

		case tmproto.FileCategory_Prescription:
			var presJSON entity.PrescriptionJSON
			if err := json.Unmarshal(jsonData, &presJSON); err != nil {
				fmt.Printf("解析处方JSON失败 %s: %v\n", file.Name(), err)
				continue
			}
			objectId = fmt.Sprintf("%v", presJSON.PresHeadID)
			eigenvalue = utils.RandomPoint()
			dataType = tmproto.DataType_TEXT

		case tmproto.FileCategory_prescription_Signature:
			var sigJSON entity.PrescriptionSignatureJSON
			if err := json.Unmarshal(jsonData, &sigJSON); err != nil {
				fmt.Printf("解析处方签名JSON失败 %s: %v\n", file.Name(), err)
				continue
			}
			objectId = fmt.Sprintf("%v", sigJSON.ID)
			eigenvalue = utils.RandomPoint()
			dataType = tmproto.DataType_TEXT

		case tmproto.FileCategory_prescription_Audit:
			var reviewJSON entity.PrescriptionReviewJSON
			if err := json.Unmarshal(jsonData, &reviewJSON); err != nil {
				fmt.Printf("解析处方审方JSON失败 %s: %v\n", file.Name(), err)
				continue
			}
			objectId = fmt.Sprintf("%v", reviewJSON.ID)
			eigenvalue = utils.RandomPoint()
			dataType = tmproto.DataType_TEXT

		case tmproto.FileCategory_Visit_Record:
			var baseJSON entity.BaseJSON
			if err := json.Unmarshal(jsonData, &baseJSON); err != nil {
				fmt.Printf("解析就诊记录JSON失败 %s: %v\n", file.Name(), err)
				continue
			}
			objectId = fmt.Sprintf("%v", baseJSON.ID)
			eigenvalue = utils.RandomPoint()
			dataType = tmproto.DataType_TEXT

		default:
			fmt.Printf("未知的文件类别: %s\n", fileCategory)
			continue
		}

		tx := &kvstore.Tx{
			OperationType: "PUT",
			OperatorID:    config.OperatorId,
			Time:          time.Now().String(),
			Data: &kvstore.FileData{
				Eigenvalue:   eigenvalue,
				ObjectID:     objectId,
				OwnerID:      objectId,
				Name:         file.Name(),
				Description:  string(jsonData),
				FileCategory: tmproto.FileCategory_name[int32(fileCategory)],
			},
			DataType: tmproto.DataType_name[int32(dataType)],
		}

		jsonTx, err := json.Marshal(tx)
		if err != nil {
			fmt.Printf("序列化Tx失败 %s: %v\n", file.Name(), err)
			continue
		}

		fmt.Printf("处理文件: %s, ObjectId: %s\n", file.Name(), objectId)

		if _, err = cli.BroadcastTxAsync(context.TODO(), jsonTx); err != nil {
			fmt.Printf("广播交易失败 %s: %v\n", file.Name(), err)
			continue
		}
	}
}
